﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Kinect;
using Microsoft.Speech.Recognition;
using System.Runtime.InteropServices;

namespace KinectInputMapper
{

    public enum DominantSide
    {
        Left,
        Right
    }

    public partial class KinectMappingForm : Form
    {
        Thread mappingThread;
        bool run = true;

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern bool SetCursorPos(int x, int y);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);

        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;

        private KinectInputLayer kinectLayer;
        private Choices commands;
        private DominantSide dominantSide;
        bool enabled = false;
        public KinectMappingForm()
        {
            InitializeComponent();

            kinectLayer = new KinectInputLayer();
            commands = new Choices();
            commands.Add("Click");
            commands.Add("Drag");
            commands.Add("Drop");
            commands.Add("Enable");
            commands.Add("Disable");

            if (kinectLayer.IsEnabled())
            {
                kinectLayer.EnableSkeletalTracking();
                kinectLayer.EnableVoiceRecognition();
                kinectLayer.SetNewSpeechVocabulary(commands);
                kinectLayer.SetLikelihoodThreshold(0.4f);
            }
            dominantSide = DominantSide.Right;

            mappingThread = new Thread(MappingLoop);
            mappingThread.Start();
            this.FormClosing += OnClose;

        }
        private void OnClose(object sender, FormClosingEventArgs e)
        {
            run = false;
            mappingThread.Abort();
        }
        private void MappingLoop()
        {
            while (run)
            {
                if (kinectLayer.IsEnabled())
                {
                    kinectLayer.Update();
                    RecognitionResult res = kinectLayer.GetNewestCommand();
                    if (res != null)
                    {
                        if (res.word == "Enable")
                            enabled = true;
                        else if (res.word == "Disable")
                            enabled = false;

                        else if (res.word == "Click")
                            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
                        else if (res.word == "Drag")
                            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);

                        else if (res.word == "Drop")
                            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);


                    }
                    if (enabled)
                    {
                        Vector2 handPos;
                        Vector2 screenDimensions = new Vector2(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                        Vector3 rawHandPos, referencePos;
                        Vector3 hipPos = kinectLayer.GetJointPosition(JointType.HipCenter);
                        float trackedWidth = kinectLayer.GetJointPosition(JointType.ShoulderRight).x - kinectLayer.GetJointPosition(JointType.ShoulderLeft).x;
                        if (dominantSide == DominantSide.Right)
                        {
                            rawHandPos = kinectLayer.GetJointPosition(JointType.HandRight);
                            referencePos = hipPos + new Vector3(trackedWidth / 2.0f, -trackedWidth / 2.0f, 0);
                        }
                        else
                        {
                            rawHandPos = kinectLayer.GetJointPosition(JointType.HandLeft);
                            referencePos = hipPos - new Vector3(trackedWidth / 2.0f, trackedWidth / 2.0f, 0);
                        }
                        Vector3 diff = rawHandPos - referencePos;
                        handPos = screenDimensions * 0.5f + new Vector2(diff.x, diff.y) * screenDimensions.x / 2;
                        SetCursorPos((int)handPos.x, (int)handPos.y);
                    }
                }
                //you need to use Invoke because the new thread can't access the UI elements directly
                MethodInvoker mi = delegate() { this.Text = DateTime.Now.ToString(); };
                this.Invoke(mi);
            }
        }
        public void SetDominantHand(DominantSide side)
        {
            dominantSide = side;
        }
        public void AddCommand(string command)
        {
            commands.Add(command);
            if (kinectLayer.IsEnabled())
                kinectLayer.SetNewSpeechVocabulary(commands);
        }

    }

}
